﻿using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Sidvall.UI.Dialogs
{
    public class DialogManager
    {
        #region Public Members

        public event System.EventHandler<DialogEventArgs> DialogOpening;
        public event System.EventHandler<DialogEventArgs> DialogClosing;

        public IFileDialog FileDialog { get; set; }
        public IFolderDialog FolderDialog { get; set; }
        public IMessageDialog MessageDialog { get; set; }
        public IMessageDialog DialogMessageDialog { get; set; }
        public ISecurityDialog SecurityDialog { get; set; }
        public IProgressDialog ProgressDialog { get; set; }
        public IWebDialog WebDialog { get; set; }
        #region DialogIdentityProviders

        private System.Lazy<Sidvall.UI.Entities.IdentityProviderCollection> _DialogIdentityProviders = new System.Lazy<Sidvall.UI.Entities.IdentityProviderCollection>();
        public Sidvall.UI.Entities.IdentityProviderCollection DialogIdentityProviders
        {
            get
            {
                return _DialogIdentityProviders.Value;
            }
        }

        #endregion

        #region LogOffAsync

        public async Task<string> LogOffAsync(LogOffDialogContext context)
        {
            if (context == null)
                return null;
            OnDialogOpening(context);
            await this.SecurityDialog.LogOnOrOffAsync(null, context);
            OnDialogClosing(context);
            return context.Response;
        }
        public async Task<string> LogOffAsync(string logOffEndpoint, string identityToken, string redirectAddress)
        {
            var context = new LogOffDialogContext()
            {
                LogOffEndpoint = logOffEndpoint,
                IdentityToken = identityToken,
                RedirectAddress = redirectAddress,
            };
            return await LogOffAsync(context);
        }

        #endregion
        #region LogOnAsync

        public async Task<Sidvall.Security.LogOnResponse> LogOnAsync(LogOnDialogContext context)
        {
            return await LogOnAsync(context, null);
        }
        public async Task<Sidvall.Security.LogOnResponse> LogOnAsync(LogOnDialogContext logOnContext, LogOffDialogContext logOffContext)
        {
            if (logOnContext == null)
                return null;
            OnDialogOpening(logOnContext);
            await this.SecurityDialog.LogOnOrOffAsync(logOnContext, logOffContext);
            OnDialogClosing(logOnContext);
            return new Sidvall.Security.LogOnResponse()
            {
                AuthorizeResponse = logOnContext.AuthorizeResponse,
                UserResponse = logOnContext.UserResponse,
                StoreResponses = logOnContext.StoreResponses,
            };
        }
        public async Task<Sidvall.Security.LogOnResponse> LogOnAsync(string authorizeEndpoint, string clientId, string responseType, string scope, string redirectAddress,
            Sidvall.Security.TwoFactorProvider twoFactorProvider, string userResponse)
        {
            return await LogOnAsync(authorizeEndpoint, clientId, responseType, scope, redirectAddress, twoFactorProvider, userResponse,
                null, null, null, null, this.DialogIdentityProviders);
        }
        public async Task<Sidvall.Security.LogOnResponse> LogOnAsync(string authorizeEndpoint, string clientId, string responseType, string scope, string redirectAddress,
            Sidvall.Security.TwoFactorProvider twoFactorProvider, string userResponse,
            string logOffEndpoint, string identityToken,
            string authorityTokenEndpoint, string clientSecret)
        {
            return await LogOnAsync(authorizeEndpoint, clientId, responseType, scope, redirectAddress, twoFactorProvider, userResponse,
                logOffEndpoint, identityToken, authorityTokenEndpoint, clientSecret, this.DialogIdentityProviders);
        }
        public async Task<Sidvall.Security.LogOnResponse> LogOnAsync(string authorizeEndpoint, string clientId, string responseType, string scope, string redirectAddress,
            Sidvall.Security.TwoFactorProvider twoFactorProvider, string userResponse,
            string logOffEndpoint, string identityToken,
            string authorityTokenEndpoint, string clientSecret, Sidvall.UI.Entities.IdentityProviderCollection identityProviders)
        {
            LogOffDialogContext logOffContext;

            var logOnContext = new LogOnDialogContext()
            {
                AuthorizeEndpoint = authorizeEndpoint,
                ClientId = clientId,
                ResponseType = responseType,
                Scope = scope,
                RedirectAddress = redirectAddress,
                State = "state",
                Nonce = "nonce",
                LogOnHint = null,
                AuthenticationContextReferences = Sidvall.Security.AuthenticationContext.GetAuthenticationContextReferences(twoFactorProvider),
                Prompt = null,
                ResponseMode = null,
                CodeChallenge = null,
                CodeChallengeMethod = null,
                AdditionalValues = null,
                UserResponse = userResponse,
                IdentityProviders = identityProviders,
            };
            if (!string.IsNullOrWhiteSpace(logOffEndpoint))
            {
                if (Sidvall.SystemContext.Current.AuthenticationStrategyManager != null)
                {
                    if (string.IsNullOrWhiteSpace(identityToken))
                    {
                        if (Sidvall.SystemContext.Current.AuthenticationStrategyManager.TokenItem == null)
                            await Sidvall.SystemContext.Current.AuthenticationStrategyManager.LoadTokenItemAsync(false);
                        identityToken = Sidvall.SystemContext.Current.AuthenticationStrategyManager.TokenItem?.IdentityToken;
                    }
                }
                logOffContext = new LogOffDialogContext()
                {
                    LogOffEndpoint = logOffEndpoint,
                    IdentityToken = identityToken,
                    RedirectAddress = redirectAddress,
                };
            }
            else
            {
                logOffContext = null;
            }
            Sidvall.Security.LogOnResponse response = null;
            try
            {
                response = await LogOnAsync(logOnContext, logOffContext);
            }
            finally
            {
                if (identityProviders != null)
                    identityProviders.Unload();
            }
            if (response?.AuthorizeResponse != null)
            {
                if (!string.IsNullOrWhiteSpace(authorityTokenEndpoint))
                {
                    response.TokenResponse = await Sidvall.Security.SecurityManager.GetTokenResponseFromCodeAsync(authorityTokenEndpoint, clientId, clientSecret,
                        response.AuthorizeResponse.Code, redirectAddress, null, null, CancellationToken.None);
                    if (Sidvall.SystemContext.Current.AuthenticationStrategyManager != null)
                    {
                        await Sidvall.SystemContext.Current.AuthenticationStrategyManager.LoadAuthenticationHeaderAsync(response.TokenResponse, response.StoreResponses);
                    }
                    if (Sidvall.SystemContext.Current.UserResponseStrategyManager != null)
                    {
                        await Sidvall.SystemContext.Current.UserResponseStrategyManager.LoadUserResponseAsync(response.UserResponse, response.StoreResponses);
                    }
                    if (Sidvall.SystemContext.Current.ProxyStrategyManager != null)
                    {
                        await Sidvall.SystemContext.Current.ProxyStrategyManager.SaveProxyAsync();
                    }
                }
            }
            return response;
        }

        #endregion

        #region OpenFileAsync

        public async Task<string> OpenFileAsync(OpenFileDialogContext context)
        {
            if (context == null)
                context = new OpenFileDialogContext();
            context.AllowMultipleSelect = false;
            OnDialogOpening(context);
            await this.FileDialog.OpenFilesAsync(context);
            OnDialogClosing(context);
            return context.FileName;
        }
        public async Task<string> OpenFileAsync(string filter, string defaultExtension)
        {
            var context = new OpenFileDialogContext()
            {
                AllowMultipleSelect = false,
                Filter = filter,
                DefaultExtension = defaultExtension,
            };
            return await OpenFileAsync(context);
        }

        #endregion
        #region OpenFilesAsync

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public async Task<string[]> OpenFilesAsync(OpenFileDialogContext context)
        {
            if (context == null)
                return null;
            OnDialogOpening(context);
            await this.FileDialog.OpenFilesAsync(context);
            OnDialogClosing(context);
            return context.FileNames.ToArray();
        }
        public async Task<string[]> OpenFilesAsync(string filter, string defaultExtension, bool allowMultipleSelect)
        {
            var context = new OpenFileDialogContext()
            {
                AllowMultipleSelect = allowMultipleSelect,
                Filter = filter,
                DefaultExtension = defaultExtension,
            };
            return await OpenFilesAsync(context);
        }

        #endregion
        #region OpenFolderAsync

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public async Task<string> OpenFolderAsync(OpenFolderDialogContext context)
        {
            if (context == null)
                return null;
            OnDialogOpening(context);
            await this.FolderDialog.OpenFolderAsync(context);
            OnDialogClosing(context);
            return context.SelectedPath;
        }
        public async Task<string> OpenFolderAsync(SpecialFolder folder, string selectedPath, bool showNewFolderButton)
        {
            var context = new OpenFolderDialogContext()
            {
                SelectedPath = selectedPath,
                SpecialFolder = folder,
                ShowNewFolderButton = showNewFolderButton,
            };
            return await OpenFolderAsync(context);
        }

        #endregion
        #region SaveFileAsync

        public async Task<string> SaveFileAsync(SaveFileDialogContext context)
        {
            if (context == null)
                context = new SaveFileDialogContext();
            OnDialogOpening(context);
            await this.FileDialog.SaveFileAsync(context);
            OnDialogClosing(context);
            return context.FileName;
        }
        public async Task<string> SaveFileAsync()
        {
            return await SaveFileAsync(new SaveFileDialogContext());
        }
        public async Task<string> SaveFileAsync(string filter, string defaultExtension)
        {
            var context = new SaveFileDialogContext()
            {
                Filter = filter,
                DefaultExtension = defaultExtension,
            };
            return await SaveFileAsync(context);
        }

        #endregion

        #region ShowDialogMessageAsync

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public async Task<MessageContext> ShowDialogMessageAsync(MessageContext message)
        {
            if (message == null)
                return null;
            OnDialogOpening(message);
            await this.DialogMessageDialog.ShowMessageAsync(message);
            OnDialogClosing(message);
            return message;
        }
        public async Task<MessageContext> ShowDialogMessageAsync(string title, string message, MessageType messageType)
        {
            var item = MessageContext.NewMessageContext(title, message, messageType);
            return await ShowDialogMessageAsync(item);
        }

        #endregion
        #region ShowDialogErrorMessageAsync

        public async Task<MessageContext> ShowDialogErrorMessageAsync(System.Exception exception)
        {
            if (exception == null)
                return null;

            var title = GetErrorTitle(exception);
            return await ShowDialogErrorMessageAsync(title, new System.Exception[] { exception });
        }
        public async Task<MessageContext> ShowDialogErrorMessageAsync(string title, System.Exception exception)
        {
            if (exception == null)
                return null;
            return await ShowDialogErrorMessageAsync(title, new System.Exception[] { exception });
        }
        public async Task<MessageContext> ShowDialogErrorMessageAsync(string title, System.Collections.Generic.IEnumerable<System.Exception> exceptions)
        {
            if (exceptions == null)
                return null;
            if (!exceptions.Any())
                return null;
            var item = MessageContext.NewMessageContext(title, Sidvall.SystemContext.Current.UserExceptionManager.GetMessage(exceptions), MessageType.Error);
            return await ShowDialogMessageAsync(item);
        }

        #endregion

        #region ShowMessageAsync

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public async Task<MessageContext> ShowMessageAsync(MessageContext message)
        {
            if (message == null)
                return null;
            OnDialogOpening(message);
            await this.MessageDialog.ShowMessageAsync(message);
            OnDialogClosing(message);
            return message;
        }
        public async Task<MessageContext> ShowMessageAsync(string title, string message, MessageType messageType)
        {
            var item = MessageContext.NewMessageContext(title, message, messageType);
            return await ShowMessageAsync(item);
        }

        #endregion
        #region ShowErrorMessageAsync

        public async Task<MessageContext> ShowErrorMessageAsync(System.Exception exception)
        {
            if (exception == null)
                return null;

            var title = GetErrorTitle(exception);
            return await ShowErrorMessageAsync(title, new System.Exception[] { exception });
        }
        public async Task<MessageContext> ShowErrorMessageAsync(string title, System.Exception exception)
        {
            if (exception == null)
                return null;
            return await ShowErrorMessageAsync(title, new System.Exception[] { exception });
        }
        public async Task<MessageContext> ShowErrorMessageAsync(string title, System.Collections.Generic.IEnumerable<System.Exception> exceptions)
        {
            if (exceptions == null)
                return null;
            if (!exceptions.Any())
                return null;
            var item = MessageContext.NewMessageContext(title, Sidvall.SystemContext.Current.UserExceptionManager.GetMessage(exceptions), MessageType.Error);
            return await ShowMessageAsync(item);
        }

        #endregion
        #region ShowOkCancelMessageAsync

        public async Task<MessageContext> ShowOkCancelMessageAsync(string title, string message, MessageType messageType, bool okIsDefault)
        {
            var item = MessageContext.NewOkCancelMessageContext(title, message, messageType, okIsDefault);
            return await ShowMessageAsync(item);
        }

        #endregion
        #region ShowYesNoMessageAsync

        public async Task<MessageContext> ShowYesNoMessageAsync(string title, string message, MessageType messageType, bool yesIsDefault)
        {
            var item = MessageContext.NewYesNoMessageContext(title, message, messageType, yesIsDefault);
            return await ShowMessageAsync(item);
        }

        #endregion
        #region ShowYesNoCancelMessageAsync

        public async Task<MessageContext> ShowYesNoCancelMessageAsync(string title, string message, MessageType messageType, bool yesIsDefault)
        {
            var item = MessageContext.NewYesNoCancelMessageContext(title, message, messageType, yesIsDefault);
            return await ShowMessageAsync(item);
        }

        #endregion

        #region ShowProgressDialogAsync

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public async Task<ProgressDialogContext> ShowProgressDialogAsync(ProgressDialogContext context)
        {
            if (context == null)
                return null;
            OnDialogOpening(context);
            await this.ProgressDialog.ShowDialogAsync(context);
            OnDialogClosing(context);
            return context;
        }

        #endregion

        #region ShowWebDialogAsync

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
        public async Task<WebDialogContext> ShowWebDialogAsync(WebDialogContext context)
        {
            if (context == null)
                return null;
            OnDialogOpening(context);
            await this.WebDialog.ShowDialogAsync(context);
            OnDialogClosing(context);
            return context;
        }
        public async Task<WebDialogContext> ShowWebDialogAsync(string title, string htmlText, string htmlSource)
        {
            var context = new WebDialogContext()
            {
                Title = title,
                HtmlText = htmlText,
                RequestMessage = new Sidvall.Net.HttpRequestMessage()
                {
                    WebAddress = htmlSource,
                },
            };
            return await ShowWebDialogAsync(context);
        }

        #endregion

        #region OnDialogOpening

        protected void OnDialogOpening(LogOffDialogContext context)
        {
            OnDialogOpening(new DialogEventArgs(context));
        }
        protected void OnDialogOpening(LogOnDialogContext context)
        {
            OnDialogOpening(new DialogEventArgs(context));
        }
        protected void OnDialogOpening(OpenFileDialogContext context)
        {
            OnDialogOpening(new DialogEventArgs(context));
        }
        protected void OnDialogOpening(SaveFileDialogContext context)
        {
            OnDialogOpening(new DialogEventArgs(context));
        }
        protected void OnDialogOpening(OpenFolderDialogContext context)
        {
            OnDialogOpening(new DialogEventArgs(context));
        }
        protected void OnDialogOpening(MessageContext message)
        {
            OnDialogOpening(new DialogEventArgs(message));
        }
        protected void OnDialogOpening(ProgressDialogContext context)
        {
            OnDialogOpening(new DialogEventArgs(context));
        }
        protected void OnDialogOpening(WebDialogContext context)
        {
            OnDialogOpening(new DialogEventArgs(context));
        }
        protected void OnDialogOpening(DialogEventArgs args)
        {
            if (this.DialogOpening != null)
                this.DialogOpening(this, args);
        }

        #endregion
        #region OnDialogClosing

        protected void OnDialogClosing(LogOffDialogContext context)
        {
            OnDialogClosing(new DialogEventArgs(context));
        }
        protected void OnDialogClosing(LogOnDialogContext context)
        {
            OnDialogClosing(new DialogEventArgs(context));
        }
        protected void OnDialogClosing(OpenFileDialogContext context)
        {
            OnDialogClosing(new DialogEventArgs(context));
        }
        protected void OnDialogClosing(SaveFileDialogContext context)
        {
            OnDialogClosing(new DialogEventArgs(context));
        }
        protected void OnDialogClosing(OpenFolderDialogContext context)
        {
            OnDialogClosing(new DialogEventArgs(context));
        }
        protected void OnDialogClosing(MessageContext message)
        {
            OnDialogClosing(new DialogEventArgs(message));
        }
        protected void OnDialogClosing(ProgressDialogContext context)
        {
            OnDialogClosing(new DialogEventArgs(context));
        }
        protected void OnDialogClosing(WebDialogContext context)
        {
            OnDialogClosing(new DialogEventArgs(context));
        }
        protected void OnDialogClosing(DialogEventArgs args)
        {
            if (this.DialogClosing != null)
                this.DialogClosing(this, args);
        }

        #endregion

        #endregion
        #region Private Members

        #region GetErrorTitle

        private static string GetErrorTitle(System.Exception exception)
        {
            if (exception == null)
                return null;
            var baseException = exception.GetBaseException();
            if (baseException is Sidvall.Security.UnauthenticatedException)
            {
                var e = baseException as Sidvall.Security.UnauthenticatedException;
                return e.GetTitleResolved();
            }
            else if (baseException is Sidvall.Security.UnauthorizedException)
            {
                var e = baseException as Sidvall.Security.UnauthorizedException;
                return e.GetTitleResolved();
            }
            else if (baseException is Sidvall.Business.BusinessException)
            {
                var e = baseException as Sidvall.Business.BusinessException;
                return e.GetTitleResolved();
            }
            else
            {
                return Sidvall.Resources.ResourceKeyManager.Current.UnexpectedExceptionTitle;
            }
        }

        #endregion

        #endregion
        #region Constructors

        public DialogManager(IFileDialog fileDialog, IFolderDialog folderDialog, IMessageDialog messageDialog, IMessageDialog dialogMessageDialog,
            ISecurityDialog securityDialog, IProgressDialog progressDialog, IWebDialog webDialog)
        {
            this.FileDialog = fileDialog;
            this.FolderDialog = folderDialog;
            this.MessageDialog = messageDialog;
            this.DialogMessageDialog = dialogMessageDialog;
            this.SecurityDialog = securityDialog;
            this.ProgressDialog = progressDialog;
            this.WebDialog = webDialog;
        }

        #endregion
    }
}
